#Mejoras Sobre Geneticonomía

import random
from copy import deepcopy, copy

inf = 2**30 #Infinito

#Funciones generales


def eleccion(lista): #cambiado: de elementos a lista
    choice = random.randint(1, 3)
    if choice == 1:
        return lista[0]
    elif choice == 2:
        return lista[1]
    elif choice == 3:
        return (lista[0] + lista[1]) / 2.0 + random.uniform(-1, 1)

#Clases y Funciones especificas
                

class Gobierno:
    """Super-clase que regula la actuación de los Agentes, ejerciendo
    su influencia a traves de impuestos y  subvenciones."""
    def __init__(self):
        self.tag = 0 #Contador de generaciones
        self.impuesto = 0.11 / 100
        self.dinero = 180000
        #self.subvencion = 0.05
        self.datos = [1, 0] #PIB, Inflación
        
    def cobrar(self, Agente):
        self.dinero += Agente.dinero * self.impuesto
        Agente.dinero -= Agente.dinero * self.impuesto

    def transferir(self, Agente, cantidad):
        self.dinero -= cantidad
        Agente.dinero += cantidad
    

    def sex2(self, prob):
        offs_imp = [0, 0]
        chance = random.randint(0, 100)
        maximo, minimo= 0, 0
        if chance <= prob:
            for i in [0,1]:
                offs_imp[i] = self.impuesto+random.uniform(-0.05/100, 0.05/100)
            if self.datos[1] > 0.02: #Inflación mayor del 2%
                minimo += 1 
            elif self.datos[1] < 0: #Deflación  
                maximo += 1 
            if self.dinero < (self.datos[0]+self.dinero) * 0.3:
                maximo += 1
            elif self.datos[0] < (self.dinero+self.datos[0])* 0.5:
                minimo += 1
            if self.dinero < 0:
                maximo += 1

            if maximo > minimo:
                self.impuesto = max(offs_imp)
            elif minimo > maximo:
                self.impuesto = min(offs_imp)

    def calcular_PIB(self, Agentes): #Sólo el dinero de los Agentes
        return sum(Agente.dinero for Agente in Agentes)#
    
    def calcular_inflacion(self, Agentes):
        PIB2 = self.calcular_PIB(Agentes)
        PIB  = self.datos[0]
        return (PIB2-PIB)/PIB
    

    def info(self, Agentes):
        self.datos[1] = self.calcular_inflacion(Agentes)
        self.datos[0] = self.calcular_PIB(Agentes)
        
        
    

class Agente:
    """Agente que ofrece su producto y demanda a otros para cubrir sus
    necesidades. Una unidad de producto cubre 1 de necesidad. Dinero,
    producto, necesidad, precio y habilidad de búsqueda son genes."""
    
    def __init__(self, dinero, producto, necesidad, precio, search):
        self.genome = [dinero, producto, necesidad, precio, search]
        self.dinero = eleccion(dinero)
        self.producto = eleccion(producto)
        self.necesidad = eleccion(necesidad)
        self.precio = eleccion(precio)
        self.search = eleccion(search)

    def __repr__(self):
        return str([self.dinero, self.producto, self.necesidad, self.precio, self.search])

    def transferencia(self, otro, cantidad):
        if self.dinero < cantidad:
            pass
        else:
            Gobierno.dinero += cantidad * Gobierno.impuesto
            otro.dinero += cantidad - cantidad * Gobierno.impuesto
            self.dinero -= cantidad

    def demandar(self):        
        visto = []
        random.shuffle(Agentes)
        visto.append(Agentes[0])
        
        for i in xrange(1, 20):#
            if random.uniform(0,100) > self.search:
                break
            else:
                visto.append(Agentes[i])
                
        while self.necesidad > 0 and self.dinero > 0 and len(visto)>0:
            mejor = min(visto, key=lambda x: x.precio)
            if mejor.producto > 0:
                self.transferencia(mejor, mejor.precio)
                mejor.producto -= 1
                self.necesidad -= 1
            else:
                visto.remove(mejor)

    def lucha(self, other, lista):
        """La forma más interesante: dejar sólo el dinero. Así desarrollan
        estrategias para conseguirlo, no sólo bajan por unos factores numéricos"""
        a = self.dinero #+ self.precio * self.producto - self.necesidad * other.precio  
        b = other.dinero #+ other.precio * other.producto - other.necesidad * self.precio  
        if a > b:
            lista.remove(other)
        elif a < b:
            lista.remove(self)

    def sex(self, other):
        offspring = [[random.choice(self.genome[i]), random.choice(other.genome[i])] for i in range(5)]
        return Agente(*offspring)

    def mutacion(self, prob): #probabilidad en porcentaje sin '%'
        chance = random.randint(0, 100)
        if chance <= prob:
            tasas = [10, 0.5, 0.5, 0.25, 0.5]
            for i in xrange(5):
                for j in xrange(2):
                    self.genome[i][j] += random.uniform(-tasas[i], tasas[i])


            
#Bucles Principales y Subprincipales
r = random.randint
Agentes = [Agente([r(800,1200),r(800,1200)], [r(50,60), r(50,60)], [r(40, 80), r(40,80)],[r(15, 25), r(15,25)],[r(1, 80),r(1, 80)]) for x in range(20)]
Gobierno = Gobierno()
def rep():#Sigo sin entender por qué hace cosas raras: 
    aux = Agentes[:]
    random.shuffle(Agentes)
    for i in xrange(0,20,2):
        Agentes[i].lucha(Agentes[i+1], aux)
    #if len(aux)%2 != 0: del aux[-1]
    for i in xrange(0,10,2):
        Agentes[i] = aux[i].sex(aux[i+1])
        Agentes[i+1] = aux[i].sex(aux[i+1])
    #del aux
    
def reproduccion():
    for i in xrange(100):#
            a = random.choice(Agentes)
            b = random.choice(Agentes)
            while b is a:
                b = random.choice(Agentes)
            a.lucha(b, Agentes)
            if Agentes.count(a) == 1 and Agentes.count(b) == 0:
                c = random.choice(Agentes)
                while c is a:
                    c = random.choice(Agentes)
                Agentes.append(a.sex(c))
            elif Agentes.count(b) == 1 and Agentes.count(a) == 0:
                c = random.choice(Agentes)
                while c is b:
                    c = random.choice(Agentes)
                Agentes.append(b.sex(c))
    
def main(n=100):
    for x in xrange(n):#
        for agente in Agentes:
            agente.demandar()
            Gobierno.cobrar(agente)
            agente.mutacion(50)
        Gobierno.info(Agentes)
        Gobierno.sex2(80)
        Gobierno.tag += 1
        rep()

def tabla():
    print "Dinero\tPrecio\tProducto\tNecesidad\tSearch"
    for a in Agentes:
        print "%s\t%s\t%s\t%s\t%s" % (str(a.dinero)[:6], round(a.precio), round(a.producto), round(a.necesidad), str(a.search)[:6])
                
def save():
    ind = []
    f = open("log.txt", "w")
    f.write(str(Gobierno.tag) +" "+ str(Gobierno.impuesto)+" "+str(Gobierno.subvencion)+ " " +
            str(Gobierno.datos[0])+" "+str(Gobierno.datos[1])+"\n")
    for Agente in Agentes:
        for i in xrange(5):
            for j in xrange(2):
                f.write(str(Agente.genome[i][j])+" ")
        f.write("\n")
    f.close()

def load():
    [Agentes.remove(Agentes[0]) for i in xrange(20)] #Agentes = [] no vale
    f = [map(float, x.split()) for x in open("log.txt")]
    Gobierno.tag, Gobierno.impuesto, Gobierno.subvencion, Gobierno.datos[0], Gobierno.datos[1] = f[0]
    for i in xrange(1,20):
        g = [f[i][j:j+2] for j in xrange(0,5*2, 2)]
        Agentes.append(Agente(g[0],g[1],g[2],g[3],g[4]))

        
            
#import timeit
#t=timeit.Timer("main()")
#t.timeit()    

